#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/kdev_t.h>
#include <linux/major.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/ctype.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/clk.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
	
#include <asm/ioctl.h>
	
#include <asm/uaccess.h>
#include <mach/gpio.h>
#include <linux/pwm.h>
#include <linux/i2c.h>
#include <linux/spi/spi.h>
#include <mach/common.h>
#include <mach/hardware.h>

#ifndef BIT
#define BIT(b)  (1<<(b))
#endif

#define MAX_Q_BUFF  0x400

typedef struct{
	int count;
	int size;
	int inp;
	int outp;
	unsigned int buff[MAX_Q_BUFF];	
}Q_t;


typedef struct spislave_handle_s{
	unsigned int cs;
	unsigned int phys_addr;
	unsigned int base;
	unsigned int irq;
	unsigned int dma;
	unsigned int mode;
	int 		 frame_size;	
	Q_t          QIn;	
	void (*rx_get) (struct spislave_handle_s *, u32 val);
	/* Function to get the data to be written to FIFO */
	u32 (*tx_get) (struct spislave_handle_s *);
}spislave_handle_t;

#define MXC_SPI_RXDATA 		0x0
#define MXC_SPI_TXDATA 		0x4
#define MXC_SPI_CTRL 		0x8
#define MXC_SPI_CONFIG 		0xc
#define MXC_SPI_STATUS		0x18
#define MXC_SPI_INTR        0x10

#define swap32(w32)   ((w32>>24) | (w32<<24) | ((w32<<8) & 0xff0000) | ((w32>>8) & 0xff00))


#define MXC_SPI_BUF_RX(type)	\
void mxc_spi_buf_rx_##type(Q_t *Q, u32 val)\
{\
	type *rx = &Q->buff[Q->inp];\
	*rx = (type)val;\
	Q->inp 	 	= (Q->inp +  sizeof(type)) %  Q->size; \
	Q->count   += sizeof(type); \
}

#define MXC_SPI_BUF_TX(type)    \
u32 mxc_spi_buf_tx_##type(Q_t *Q)\
{\
	type *tx = (type *)&Q->buff[Q->outp];\
	type val = *tx;\
	Q->outp   = (Q->outp +  sizeof(type)) %  Q->size; \
	Q->count  -= sizeof(type); \
	return val; \
}

MXC_SPI_BUF_RX(u8)
MXC_SPI_BUF_TX(u8)
MXC_SPI_BUF_RX(u16)
MXC_SPI_BUF_TX(u16)
MXC_SPI_BUF_RX(u32)
MXC_SPI_BUF_TX(u32)


static irqreturn_t mxc_spi_isr(int irq, void *dev_id)
{
	spislave_handle_t *pSpi = (spislave_handle_t *)dev_id;
	irqreturn_t ret = IRQ_NONE;
	unsigned int status;
	unsigned int rx_data; 
	do{
		rx_data = __raw_readl(pSpi->base + MXC_SPI_RXDATA); //drain rx_data register
		pSpi->rx_get(&pSpi->QIn,rx_data);
		status = __raw_readl(pSpi->base + MXC_SPI_STATUS); 
	}while(status & BIT(3));
	return IRQ_HANDLED;
	
}


int  SpiSlave_Init(int spi,int mode,int frame_size ,unsigned int *pRethandle ){
		int ret=0;
		spislave_handle_t *pSpi;
		unsigned int ctrl=0;
		unsigned int cfg=0;
		pSpi	= kzalloc(sizeof(spislave_handle_t), GFP_KERNEL);
		pSpi->frame_size = frame_size;
		pSpi->mode = mode;
		
		switch(spi){
			case 0 :
			pSpi->phys_addr	= 0x50010000;
			pSpi->irq  =    36;
			printk("%s: phys(%x)\n\r",__func__,pSpi->phys_addr);
			break;					
			case 1 :
			//	pSpi->phys_addr	= CSPI2_BASE_ADDR;
				//pSpi->base =	ioremap(CSPI2_BASE_ADDR,0x1000);	
			//	pSpi->irq  =	MXC_INT_CSPI2;	
			break;					
			case 2 :
				
				//pSpi->base =	CSPI3_BASE_ADDR;	
				//pSpi->irq  =	MXC_INT_CSPI3;	
			break;					
			case 3 :
			break;	
		}

		ret = request_mem_region(pSpi->phys_addr,0x1000,"mxc_spi");
		pSpi->base = ioremap(pSpi->phys_addr,0x1000);
				
        
		
#if defined(USE_ISR)	
		ret = request_irq(pSpi->irq, mxc_spi_isr,  0, "CSPI_IRQ", pSpi);
#endif
		*pRethandle = (unsigned int)pSpi;
		ctrl = (pSpi->frame_size -1)<<20;
		//__raw_writel(0, pSpi->base + MXC_SPI_CTRL);
 		//msleep(100);
		__raw_writel(ctrl | 1, pSpi->base + MXC_SPI_CTRL);		
		msleep(10);
		__raw_writel(pSpi->mode, pSpi->base + MXC_SPI_CONFIG);		

		printk("%s.%d phys(%x) [0x%x,0x%x,0x%x ]\n\r",__func__,__LINE__,pSpi->phys_addr,__raw_readl(pSpi->base + MXC_SPI_STATUS),__raw_readl(pSpi->base + MXC_SPI_CONFIG),__raw_readl(pSpi->base + MXC_SPI_CTRL));		


	//	__raw_writel(BIT(3), pSpi->base + MXC_SPI_INTR);		

		//printk("%s.%d phys(%x) [0x%x,0x%x,0x%x ]\n\r",__func__,__LINE__,pSpi->phys_addr,__raw_readl(pSpi->base + MXC_SPI_STATUS),__raw_readl(pSpi->base + MXC_SPI_CONFIG),__raw_readl(pSpi->base + MXC_SPI_CTRL));
		return ret;	
}	


int  SpiSlave_Term(unsigned int *handle ){
	spislave_handle_t *pSpi = (spislave_handle_t *)handle;
	if(pSpi->base)
		iounmap(pSpi->base);

	release_mem_region(pSpi->phys_addr,0x1000);
	free_irq(pSpi->irq, pSpi);
	__raw_writel(0, pSpi->base + MXC_SPI_CTRL);	
	kfree(pSpi);
	return 0;
}



int SpiSlave_Read(unsigned int *handle,void *buff,int count){
	spislave_handle_t *pSpi = (spislave_handle_t *)handle;
	int i=0;
	
#if defined(USE_ISR)	
	while(pSpi->QIn.count>0){
		buff[i]= pSpi->QIn.buff[pSpi->QIn.outp];
		pSpi->QIn.outp = (pSpi->QIn.outp + 1) pSpi->QIn.size;
		pSpi->QIn.count--;
		i++;
	}
#else
	
	switch(pSpi->frame_size){
		case 0 :
		case 8 :
			{ 
				unsigned int status=0;				
				unsigned char *pBuf = (unsigned char *)buff;
				unsigned int rx_data=0;
				i =0;
				do {	
					rx_data = __raw_readl(pSpi->base + MXC_SPI_RXDATA); //drain rx_data register	
					pBuf[i++] = rx_data;
					if(i==count)
						break;
				 status = 	__raw_readl(pSpi->base + MXC_SPI_STATUS); 
				 
				}while(status & BIT(3));
			}
			break;
		case 16 :
			{ 
				unsigned int status=0;				
				unsigned short *pBuf  = (unsigned short *)buff;
				unsigned int rx_data=0;
				i =0;
				do {	
					rx_data = __raw_readl(pSpi->base + MXC_SPI_RXDATA); //drain rx_data register	
					pBuf[i++] = rx_data;
					if(i==count/2)
						break;
				 status = 	__raw_readl(pSpi->base + MXC_SPI_STATUS); 	
				}while(status & BIT(3));
			}
			break;				
			
		case 32 :
			{ 
				unsigned int status=0;				
				unsigned int *pBuf = (unsigned short *)buff;
				unsigned int rx_data=0;
				i =0;
				for(i=0;i<count/4;i++){	
					status = 	__raw_readl(pSpi->base + MXC_SPI_STATUS); 	
					// printk("%s.%d %x\n\r",__func__,__LINE__,status);					
					if((status & BIT(3)) ==0)
						break;
					rx_data = __raw_readl(pSpi->base + MXC_SPI_RXDATA); //drain rx_data register	
					pBuf[i] = swap32(rx_data);
				}
				i = i *4;
			}
			break;
	}
		

#endif	
	return i;
}

int SpiSlave_Write(unsigned int *handle,void *buff,int count){
		spislave_handle_t *pSpi = (spislave_handle_t *)handle;
		unsigned int data;
		int i=0;
		switch(pSpi->frame_size){
			case 0 :
			case 8 :{	
				unsigned char *pBuf = (unsigned char *)buff;
				for (i = 0; i < count; i++) {
					__raw_writel(pBuf[i], pSpi->base + MXC_SPI_TXDATA);
				}
				}
				break;
			case 16 :{	
				unsigned short *pBuf = (unsigned short *)buff;
					for (i = 0; i < count/2; i++) {
						__raw_writel(pBuf[i], pSpi->base + MXC_SPI_TXDATA);
					}
				}
				break;
				
			case 32 :{	
				
					unsigned int status=0;				
					unsigned int *pBuf = (unsigned int *)buff;
					for (i = 0; i < count/4; i++) {
						status = 	__raw_readl(pSpi->base + MXC_SPI_STATUS);						
						if(status & BIT(2)) //tx fifo full;
							break;
						__raw_writel(swap32(pBuf[i]), pSpi->base + MXC_SPI_TXDATA);
						//__raw_writel(pBuf[i], pSpi->base + MXC_SPI_TXDATA);

					}
					
					i = i *4;
					
				}
			
				//while()
				break;
		}
		return i;
		return 0;
}

unsigned int SpiSlave_Status(unsigned int *handle){
	spislave_handle_t *pSpi = (spislave_handle_t *)handle;	
	unsigned status;	
	status =	__raw_readl(pSpi->base + MXC_SPI_STATUS);
	return status;
}





